<?php

/**
 * LEAGUEMan
 * 
 * Copyright (C) 2007 Marko Bischof
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

abstract class LM_Table
{
    protected $_tbl = '';
    protected $_tbl_key = '';
    protected $_error = '';
    protected $_db = null;
    protected $_where = array();

    protected function __construct($table, $key, $db)
    {
        $this->_tbl = $table;
        $this->_tbl_key = $key;
        $this->_db = $db;
        $this->_where = array();
    }

    public function getDB()
    {
        return $this->_db;
    }

    public function getPublicProperties()
    {
        static $cache = null;
        if (is_null($cache)) {
            $cache = array();
            foreach (get_class_vars(get_class($this)) as $key => $val) {
                if (substr($key, 0, 1) != '_') {
                    $cache[] = $key;
                }
            }
        }
        return $cache;
    }

    function getQuery()
    {
        return $this->_db->getQuery();
    }

    public function populate($o)
    {
        if (is_object($o)) {

            $class_vars = get_class_vars(get_class($this));

            foreach ($o AS $var => $value) {
                if (array_key_exists($var, $class_vars)) {
                    $this->$var = $value;
                }
            }
        }
    }

    public function filter($ignoreList=null)
    {
        $ignore = is_array($ignoreList);

        $iFilter = new InputFilter();
        foreach ($this->getPublicProperties() as $k) {
            if ($ignore && in_array($k, $ignoreList)) {
                continue;
            }
            $this->$k = $iFilter->process($this->$k);
        }
    }

    public function getError()
    {
        return $this->_error;
    }

    public function get($_property)
    {
        if (isset($this->$_property)) {
            return $this->$_property;
        } else {
            return null;
        }
    }

    public function set($_property, $_value)
    {
        $this->$_property = $_value;
    }

    public function reset($value = null)
    {
        $keys = $this->getPublicProperties();
        foreach ($keys as $k) {
            $this->$k = $value;
        }
    }

    public function bind($array, $ignore='')
    {
        if (!is_array($array)) {
            $this->_error = strtolower(get_class($this)) . "::bind failed.";
            return false;
        } else {
            return mosBindArrayToObject($array, $this, $ignore);
        }
    }

    public function load($oid=null)
    {
        $k = $this->_tbl_key;

        if ($oid !== null) {
            $this->$k = $oid;
        }

        $oid = $this->$k;

        if ($oid === null) {
            return false;
        }

        $class_vars = get_class_vars(get_class($this));
        foreach ($class_vars as $name => $value) {
            if (($name != $k) and ($name != "_db") and ($name != "_tbl") and ($name != "_tbl_key")) {
                $this->$name = $value;
            }
        }

        $this->reset();

        $query = "SELECT *"
                . "\n FROM $this->_tbl"
                . "\n WHERE $this->_tbl_key = '$oid'"
        ;
        $this->_db->setQuery($query);

        return $this->_db->loadObject($this);
    }

    public function check()
    {
        return true;
    }

    public function store($updateNulls = false)
    {
        $k = $this->_tbl_key;

        if ($this->$k) {
            $ret = $this->_db->updateObject($this->_tbl, $this, $this->_tbl_key, $updateNulls);
        } else {
            $ret = $this->_db->insertObject($this->_tbl, $this, $this->_tbl_key);
        }
        if (!$ret) {
            $this->_error = strtolower(get_class($this)) . "::store failed <br />" . $this->_db->getErrorMsg();
            return false;
        } else {
            return true;
        }
    }

    public function move($dirn, $where='')
    {
        $k = $this->_tbl_key;

        $sql = "SELECT $this->_tbl_key, ordering FROM $this->_tbl";

        if ($dirn < 0) {
            $sql .= "\n WHERE ordering < $this->ordering";
            $sql .= ( $where ? "\n	AND $where" : '');
            $sql .= "\n ORDER BY ordering DESC";
            $sql .= "\n LIMIT 1";
        } else if ($dirn > 0) {
            $sql .= "\n WHERE ordering > $this->ordering";
            $sql .= ( $where ? "\n	AND $where" : '');
            $sql .= "\n ORDER BY ordering";
            $sql .= "\n LIMIT 1";
        } else {
            $sql .= "\nWHERE ordering = $this->ordering";
            $sql .= ( $where ? "\n AND $where" : '');
            $sql .= "\n ORDER BY ordering";
            $sql .= "\n LIMIT 1";
        }

        $this->_db->setQuery($sql);

        $row = null;
        if ($this->_db->loadObject($row)) {
            $query = "UPDATE $this->_tbl"
                    . "\n SET ordering = '$row->ordering'"
                    . "\n WHERE $this->_tbl_key = '" . $this->$k . "'"
            ;
            $this->_db->setQuery($query);

            if (!$this->_db->query()) {
                $err = $this->_db->getErrorMsg();
                die($err);
            }

            $query = "UPDATE $this->_tbl"
                    . "\n SET ordering = '$this->ordering'"
                    . "\n WHERE $this->_tbl_key = '" . $row->$k . "'"
            ;
            $this->_db->setQuery($query);

            if (!$this->_db->query()) {
                $err = $this->_db->getErrorMsg();
                die($err);
            }

            $this->ordering = $row->ordering;
        } else {
            $query = "UPDATE $this->_tbl"
                    . "\n SET ordering = '$this->ordering'"
                    . "\n WHERE $this->_tbl_key = '" . $this->$k . "'"
            ;
            $this->_db->setQuery($query);

            if (!$this->_db->query()) {
                $err = $this->_db->getErrorMsg();
                die($err);
            }
        }
    }

    public function updateOrder($where = '')
    {
        $k = $this->_tbl_key;

        if (!array_key_exists('ordering', get_class_vars(strtolower(get_class($this))))) {
            $this->_error = "WARNING: " . strtolower(get_class($this)) . " does not support ordering.";
            return false;
        }

        $query = "SELECT $this->_tbl_key, ordering"
                . "\n FROM $this->_tbl"
                . ( $where ? "\n WHERE $where" : '' )
                . "\n ORDER BY ordering"
        ;
        $this->_db->setQuery($query);
        if (!($orders = $this->_db->loadObjectList())) {
            $this->_error = $this->_db->getErrorMsg();
            return false;
        }

        for ($i = 0; $i < count($orders); $i++) {
            if ($orders[$i]->ordering >= 0) {
                $orders[$i]->ordering = $i + 1;
            }
        }

        $shift = 0;
        $n = count($orders);
        for ($i = 0; $i < $n; $i++) {
            if ($orders[$i]->$k == $this->$k) {
                $orders[$i]->ordering = min($this->ordering, $n);
                $shift = 1;
            } else if ($orders[$i]->ordering >= $this->ordering && $this->ordering > 0) {
                $orders[$i]->ordering++;
            }
        }
        for ($i = 0, $n = count($orders); $i < $n; $i++) {
            if ($orders[$i]->ordering >= 0) {
                $orders[$i]->ordering = $i + 1;
                $query = "UPDATE $this->_tbl"
                        . "\n SET ordering = '" . $orders[$i]->ordering . "'"
                        . "\n WHERE $k = '" . $orders[$i]->$k . "'"
                ;
                $this->_db->setQuery($query);
                $this->_db->query();
            }
        }

        if ($shift == 0) {
            $order = $n + 1;
            $query = "UPDATE $this->_tbl"
                    . "\n SET ordering = '$order'"
                    . "\n WHERE $k = '" . $this->$k . "'"
            ;
            $this->_db->setQuery($query);
            $this->_db->query();
        }
        return true;
    }

    public function canDelete($oid=null, $joins=null)
    {
        $k = $this->_tbl_key;
        if ($oid) {
            $this->$k = intval($oid);
        }
        if (is_array($joins)) {
            $select = $k;
            $join = '';
            foreach ($joins as $table) {
                $select .= ",\n COUNT(DISTINCT {$table['idfield']}) AS {$table['idfield']}";
                $join .= "\n LEFT JOIN {$table['name']} ON {$table['joinfield']} = $k";
            }

            $query = "SELECT $select"
                    . "\n FROM $this->_tbl"
                    . $join
                    . "\n WHERE $k = " . $this->$k
                    . "\n GROUP BY $k"
            ;
            $this->_db->setQuery($query);

            if ($obj = $this->_db->loadObject()) {
                $this->_error = $this->_db->getErrorMsg();
                return false;
            }
            $msg = array();
            foreach ($joins as $table) {
                $k = $table['idfield'];
                if ($obj->$k) {
                    $msg[] = $AppUI->_($table['label']);
                }
            }

            if (count($msg)) {
                $this->_error = "noDeleteRecord" . ": " . implode(', ', $msg);
                return false;
            } else {
                return true;
            }
        }

        return true;
    }

    public function delete($oid=null)
    {

        $k = $this->_tbl_key;
        if ($oid) {
            $this->$k = intval($oid);
        }

        $query = "DELETE FROM $this->_tbl WHERE $this->_tbl_key = '" . $this->$k . "'";
        $this->_db->setQuery($query);

        if ($this->_db->query()) {
            return true;
        } else {
            $this->_error = $this->_db->getErrorMsg();
            return false;
        }
    }

    public function hit($oid = null)
    {

        $k = $this->_tbl_key;
        if ($oid !== null) {
            $this->$k = intval($oid);
        }

        $query = "UPDATE $this->_tbl SET hits = ( hits + 1 ) WHERE $this->_tbl_key = '" . $this->$k . "'";
        $this->_db->setQuery($query);
        $this->_db->query();
    }

    public function save($source, $order_filter = '')
    {
        if (!$this->bind($source)) {
            return false;
        }
        if (!$this->check()) {
            return false;
        }
        if (!$this->store()) {
            return false;
        }

        if ($order_filter) {
            $filter_value = $this->$order_filter;
            $this->updateOrder($order_filter ? "`$order_filter` = '$filter_value'" : '');
        }
        $this->_error = '';
        return true;
    }

    public function publish($cid = null, $publish = 1, $user_id = 0)
    {
        mosArrayToInts($cid, array());
        $user_id = intval($user_id);
        $publish = intval($publish);

        if (count($cid) < 1) {
            $this->_error = "No items selected.";
            return false;
        }

        $cids = $this->_tbl_key . '=' . implode(' OR ' . $this->_tbl_key . '=', $cid);

        $query = "UPDATE $this->_tbl SET published = " . intval($publish) . " WHERE ($cids)";
        $this->_db->setQuery($query);
        if (!$this->_db->query()) {
            $this->_error = $this->_db->getErrorMsg();
            return false;
        }

        $this->_error = '';
        return true;
    }

    public function addFilter($filter)
    {
        $this->_where[] = $filter;
    }

    public function getFilter()
    {
        return count($this->_where) > 0 ? " WHERE " . implode(" AND ", $this->_where) : " ";
    }

}